package web.java.a_connectionPool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

class Pool{
	/*
	 * 1、指定全局参数
	 * */
	private int init_count=3;	//默认连接池大小为3
	private int current_count=0;	//当前连接个数
	private int max_count=6;	//最大连接个数，超过最大连接个数则中断操作并报异常
	private LinkedList<Connection> pools=new LinkedList<Connection>();	//连接对象集合
	/*
	 * 3、构造方法，创建三个数据库连接对象放入连接池中
	 * */
	public Pool() {
		for(int i=0;i<init_count;i++){
			//创建三个连接对象，放入连接池中
			Connection conn=createConnection();
			pools.addLast(conn);
		}
		System.out.println("当前连接个数为："+current_count);
		System.out.println("连接池个数为："+pools.size());
	}
	
	/*
	 * 2、创建数据库连接对象，并使用代理模式给关闭操作添加新的方法
	 * 		主要给原来的方法提供新的操作
	 * 		比如close方法，当conn执行close方法时，并不会把连接从连接池中释放，而是直接消除，
	 * 		所以，当conn执行到close方法时，需要让塔执行自定义的release方法，来释放连接
	 * 
	 * 注意：代理方法需要写在创建Connection对象方法中，保证每一个数据库的连接对象都是代理对象
	 * 
	 * */
	private Connection createConnection(){
		try {
			//2.1 连接数据库
			Class.forName("com.mysql.jdbc.Driver");
			final Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/shop","root","root");
			//2.2 对conn创建其代理对象
			Connection proxy=(Connection)Proxy.newProxyInstance(
					conn.getClass().getClassLoader(),	//类加载器，加载对象类的字节码文件
					new Class[]{Connection.class},		//目标对象实现的接口
					new InvocationHandler(){	//当调用conn对象方法的时候，自动触发事务处理器

						@Override
						public Object invoke(Object proxy, Method method,
								Object[] args) throws Throwable {
							Object result = null;	//方法的返回值，在Connection接口中，有的方法有返回值
							//2.2.1 获取当前对象调用的方法名称，这里是调用close方法时添加新的操作
							String methodName=method.getName();
							System.out.println("执行代理...");
							if("close".equals(methodName)){
								System.out.println(methodName+"开始执行...");
								//2.2.2 释放连接
								releaseConnection(conn);
							}else{
								//2.2.3 其他的方法按照父类的方法执行
								result=method.invoke(conn, args);
							}
							return result;
						}}		
			);
			//2.3 返回代理后的连接对象
			return proxy;
		} catch (Exception e) {
			throw new RuntimeException("创建数据库异常！"+e.getMessage());
		}
		
	}
	/*
	 * 4、获取连接对象，将连接对象给用户
	 * 	每分配一个连接给用户，都要对连接池和最大连接数进行判断
	 * */
	public Connection getConnection(){
		//判断连接池，当前连接数小于连接池大小时，分配给用户连接池中的连接对象
		if(current_count < init_count){
			//当前连接数+1
			current_count++;
			Connection conn=pools.removeFirst();
			System.out.println("当前连接个数为："+current_count);
			System.out.println("连接池个数为："+pools.size());
			return conn;	//返回一个连接对象，连接池个数-1
		}
		//判断最大连接数，当前连接数大于连接池大小但小于最大连接数时，创建一个新的连接给用户
		if(current_count < max_count){
			//当前连接数+1
			current_count++;
			Connection conn=createConnection();
			System.out.println("当前连接个数为："+current_count);
			System.out.println("连接池个数为："+pools.size());
			return conn;	//创建一个连接对象返回
		}
		//超出最大连接数，则直接报异常
		throw new RuntimeException("当前连接数超出最大连接数！");
	}
	/*
	 * 5、释放连接
	 * 	对连接个数进行判断，将释放掉的连接放入连接池，或直接关闭
	 * */
	public void releaseConnection(Connection conn){
		//若当前连接个数小于连接池大小，说明是从连接池取出的连接对象，需要放回到连接池中
		if(current_count <= init_count ){
			//当前连接数-1
			current_count--;
			//将连接放回到连接池
			pools.addLast(conn);
		}else{
			//直接关闭
			current_count--;
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("当前连接个数为："+current_count);
		System.out.println("连接池个数为："+pools.size());
	}
}

public class Demo02_Proxy {
	public static void main(String[] args) throws SQLException {
		Pool pool=new Pool();
		pool.getConnection();
		pool.getConnection();
		pool.getConnection();
		pool.getConnection();
		pool.getConnection();
		Connection conn=pool.getConnection();
		conn.close();
		pool.getConnection();
	}
}
